package org.jsets.fastboot.persistent.impl;

import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.javers.core.ChangesByCommit;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Diff;
import org.javers.core.diff.ListCompareAlgorithm;
import org.javers.core.metamodel.object.CdoSnapshot;
import org.javers.repository.jql.JqlQuery;
import org.javers.repository.jql.QueryBuilder;
import org.javers.repository.sql.DialectName;
import org.javers.repository.sql.JaversSqlRepository;
import org.javers.repository.sql.SqlRepositoryBuilder;
import org.jsets.fastboot.util.SpringContextHolder;
import org.jsets.fastboot.util.StringUtils;
import org.jsets.fastboot.persistent.config.PersistentProperties;
import com.alibaba.druid.pool.DruidDataSource;

public class JaversService {
	
	private static final String MYSQL = "MySQL";
	private static final String ORACLE = "Oracle";

	private Javers javers;
	private JaversConnectionProvider connectionProvider;
	private final PersistentProperties properties;
	
	public JaversService(PersistentProperties properties) {
		this.properties = properties;
	}

	public void afterInitialize() {
		if (StringUtils.hasText(this.properties.getJavers().getDriverClassName())
				&& StringUtils.hasText(this.properties.getJavers().getUrl())
				&& StringUtils.hasText(this.properties.getJavers().getUsername())
				&& StringUtils.hasText(this.properties.getJavers().getPassword())) {
			DruidDataSource druidDataSource = new DruidDataSource();
			druidDataSource.setDriverClassName(this.properties.getJavers().getDriverClassName());
			druidDataSource.setUrl(this.properties.getJavers().getUrl());
			druidDataSource.setUsername(this.properties.getJavers().getUsername());
			druidDataSource.setPassword(this.properties.getJavers().getPassword());
			this.connectionProvider = new JaversConnectionProvider(druidDataSource);
		}else {
			DataSource dataSource = SpringContextHolder.tryBean(DataSource.class);
			this.connectionProvider = new JaversConnectionProvider(dataSource);
		}
		
		final DialectName dialectName = this.getDialectName();
		final JaversSqlRepository repository = SqlRepositoryBuilder
				.sqlRepository()
				.withConnectionProvider(this.connectionProvider)
				.withDialect(dialectName)
				//.withSchema("fastboot")
				.withSchemaManagementEnabled(false)
				.build();
		this.javers = JaversBuilder
				.javers()
				.withListCompareAlgorithm(ListCompareAlgorithm.LEVENSHTEIN_DISTANCE)
				.registerJaversRepository(repository)
				.build();
	}
	
	public Javers getJavers() {
		return javers;
	}
	
	/**
	 * 获取实体变更轨迹
	 * @param id 实体主键
	 * @param clazz 实体类
	 * @return 变更信息
	 */
	public List<ChangesByCommit> findChangesById(Object id, Class<?> clazz) {
		JqlQuery query = QueryBuilder.byInstanceId(id, clazz).build();
		return this.getJavers().findChanges(query).groupByCommit();
	}
	
	public List<ChangesByCommit> findChangesByJqlQuery(JqlQuery query) {
		return this.getJavers().findChanges(query).groupByCommit();
	}
	
	/**
	 * 获取数据版本快照
	 * @param id 实体主键
	 * @param clazz 实体类
	 * @return 快照
	 */
	public List<CdoSnapshot> findSnapshotsById(Object id, Class<?> clazz) {
		JqlQuery query = QueryBuilder.byInstanceId(id, clazz).build();
		return this.getJavers().findSnapshots(query);
	}
	
	public List<CdoSnapshot> findSnapshotsByJqlQuery(JqlQuery query) {
		return this.getJavers().findSnapshots(query);
	}
	
	/**
	 * 对象比较
	 * @param oldVersion 原对象
	 * @param currentVersion 当前对象
	 * @return 差异信息
	 */
	public Diff compare(Object oldVersion, Object currentVersion) {
		return this.getJavers().compare(oldVersion, currentVersion);
	}
	
	private DialectName getDialectName() {
		String productName = "";
		try {
			productName = this.connectionProvider.getConnection().getMetaData().getDatabaseProductName();
		} catch (SQLException e) {
			// 忽略
		}
		switch (productName) {
		case MYSQL:
			return DialectName.MYSQL;
		case ORACLE:
			return DialectName.ORACLE;
		default:
			throw new RuntimeException("不支持的数据库类型");
		}
	}

}